package utils

import (
	"context"
	"time"

	"github.com/go-redis/redis/v8"
)

var redisClient *RedisClient

// InitRedisClient 初始化 redis
func InitRedisClient(address, password string, db int) (*RedisClient, error) {
	redisClient = &RedisClient{
		Context: context.Background(),
		Client: redis.NewClient(&redis.Options{
			Addr: address, // 要连接的redis IP:port
			//Username: "default",
			Password: password, // redis 密码
			DB:       db,       // 要连接的redis 库
			PoolSize: 100,      //连接池大小
		}),
	}

	// 检测心跳
	if err := redisClient.Ping(); err != nil {
		return redisClient, err
	}

	return redisClient, nil
}

type RedisClient struct {
	Client  *redis.Client
	Context context.Context
}

// Ping 用以测试 redis 连接是否正常
func (rds RedisClient) Ping() error {
	_, err := rds.Client.Ping(rds.Context).Result()
	return err
}

func (rds RedisClient) Close() error {
	err := rds.Client.Close()
	return err
}

// SetNX 存储 key 对应的 value，且设置 expiration 过期时间
func (rds RedisClient) SetNX(key string, value interface{}, expiration time.Duration) error {
	return rds.Client.SetNX(rds.Context, key, value, expiration).Err()
}

// Set 存储 key 对应的 value，且设置 expiration 过期时间
func (rds RedisClient) Set(key string, value interface{}, expiration time.Duration) error {
	return rds.Client.Set(rds.Context, key, value, expiration).Err()
}

// Get 获取 key 对应的 value，内部错误和 redis.Nil 都返回 默认值
func (rds RedisClient) Get(key string, defauleValue ...string) string {
	result, err := rds.Client.Get(rds.Context, key).Result()
	if err != nil {
		if len(defauleValue) == 1 {
			return defauleValue[0]
		}
		return ""
	}
	return result
}

// Has 判断一个 key 是否存在，内部错误和 redis.Nil 都返回 false
func (rds RedisClient) Has(key string) bool {
	_, err := rds.Client.Get(rds.Context, key).Result()
	if err != nil {
		return false
	}
	return true
}

// Del 删除存储在 redis 里的数据，支持多个 key 传参
func (rds RedisClient) Del(keys ...string) bool {
	if err := rds.Client.Del(rds.Context, keys...).Err(); err != nil {
		return false
	}
	return true
}

// FlushDB 清空当前 redis db 里的所有数据
func (rds RedisClient) FlushDB() bool {
	if err := rds.Client.FlushDB(rds.Context).Err(); err != nil {
		return false
	}
	return true
}

// Increment 当参数只有 1 个时，为 key，其值增加 1。
// 当参数有 2 个时，第一个参数为 key ，第二个参数为要增加的值 int64 类型。
func (rds RedisClient) Increment(parameters ...interface{}) bool {
	switch len(parameters) {
	case 1:
		key := parameters[0].(string)
		if err := rds.Client.Incr(rds.Context, key).Err(); err != nil {
			return false
		}
	case 2:
		key := parameters[0].(string)
		value := parameters[1].(int64)
		if err := rds.Client.IncrBy(rds.Context, key, value).Err(); err != nil {
			return false
		}
	default:
		return false
	}
	return true
}

// Decrement 当参数只有 1 个时，为 key，其值减去 1。
// 当参数有 2 个时，第一个参数为 key ，第二个参数为要减去的值 int64 类型。
func (rds RedisClient) Decrement(parameters ...interface{}) bool {
	switch len(parameters) {
	case 1:
		key := parameters[0].(string)
		if err := rds.Client.Decr(rds.Context, key).Err(); err != nil {
			return false
		}
	case 2:
		key := parameters[0].(string)
		value := parameters[1].(int64)
		if err := rds.Client.DecrBy(rds.Context, key, value).Err(); err != nil {
			return false
		}
	default:
		return false
	}
	return true
}

// Publish (发布/订阅模式)发布消息到redis
func (rds RedisClient) Publish(channel string, msg string) error {
	return rds.Client.Publish(rds.Context, channel, msg).Err()
}

// Subscribe (发布/订阅模式)订阅redis消息
func (rds RedisClient) Subscribe(channel ...string) (string, error) {
	sub := rds.Client.Subscribe(rds.Context, channel...)
	msg, err := sub.ReceiveMessage(rds.Context)
	return msg.Payload, err
}
